import { ImportUtil } from 'babel-import-util';

function parentIsUnary(node) {
  if (node.parent.type === 'UnaryExpression' && node.parent.operator === '!') {
    return true;
  }
  return false;
}

export default function (babel) {
  const { types: t } = babel;

  return {
    name: 'ast-transform', // not required
    visitor: {
      ImportDeclaration(path, state) {
        const importPath = path.node.source.value;

        if (state.opts.sources.includes(importPath)) {
          const specifiers = path.get('specifiers');
          specifiers.forEach((specifier) => {
            let name = specifier.node.imported.name;
            if (!(name in state.opts.flags)) {
              throw new Error(`Unexpected flag ${name} imported from ${importPath}`);
            }
            let localBindingName = specifier.node.local.name;
            let binding = specifier.scope.getBinding(localBindingName);
            const enableRuntimeActivation = Boolean(state.opts.runtimeKey);
            const strippableKey = enableRuntimeActivation ? state.opts.runtimeKey : state.opts.configKey;

            binding.referencePaths.forEach((p) => {
              let negateStatement = false;
              let node = p;
              if (parentIsUnary(p)) {
                negateStatement = true;
                node = p.parentPath;
              }
              let getConfig = t.memberExpression(
                t.memberExpression(
                  t.memberExpression(
                    t.callExpression(state.importer.import(p, '@embroider/macros', 'getGlobalConfig'), []),
                    t.identifier('WarpDrive')
                  ),
                  t.identifier(strippableKey)
                ),
                t.identifier(name)
              );

              node.replaceWith(
                // if (LOG_FOO) {
                //   // ...
                // }
                // =>
                // if (macroCondition(getGlobalConfig('WarpDrive').debug.LOG_FOO)) {
                //     // ...
                // }
                t.callExpression(state.importer.import(p, '@embroider/macros', 'macroCondition'), [
                  negateStatement ? t.unaryExpression('!', getConfig) : getConfig,
                ])
              );

              if (enableRuntimeActivation) {
                // we do not yet support arbitrary runtime activation locations,
                // the only supported locations are `if (LOG)` style statements, no
                // ternaries or other more complex expressions
                const parentIfStatement = node.parentPath.type === 'IfStatement' ? node.parentPath : null;
                if (!parentIfStatement) {
                  throw new Error(
                    `Runtime activation of logging flags is only supported in if statements, but found node '${node.parentPath.type}'`
                  );
                }

                // if (LOG_FOO) {
                //   // ...
                // }
                // =>
                // if (macroCondition(getGlobalConfig('WarpDrive').activeLogging.LOG_FOO)) {
                //   if (getGlobalConfig('WarpDrive').debug.LOG_FOO || globalThis.getWarpDriveRuntimeConfig().debug.LOG_FOO) {
                //     // ...
                //   }
                // }
                //
                // the outer-if is generated by the node-replace above. The inner if is generated here.
                const originalBody = parentIfStatement.node.consequent;

                // getGlobalConfig('WarpDrive').debug.LOG_FOO
                const getActualConfig = t.memberExpression(
                  t.memberExpression(
                    t.memberExpression(
                      t.callExpression(state.importer.import(p, '@embroider/macros', 'getGlobalConfig'), []),
                      t.identifier('WarpDrive')
                    ),
                    t.identifier(state.opts.configKey)
                  ),
                  t.identifier(name)
                );

                // globalThis.getWarpDriveRuntimeConfig().debug.LOG_FOO
                const getRuntimeConfig = t.memberExpression(
                  t.memberExpression(
                    t.callExpression(
                      t.memberExpression(t.identifier('globalThis'), t.identifier('getWarpDriveRuntimeConfig')),
                      []
                    ),
                    t.identifier(state.opts.configKey)
                  ),
                  t.identifier(name)
                );

                // <getActualConfig> || <getRuntimeConfig>
                const ifExp = t.logicalExpression('||', getActualConfig, getRuntimeConfig);

                // if (<negateStatement>(<getActualConfig> || <getRuntimeConfig>)) <originalBody>
                const innerIfStatement = t.ifStatement(
                  negateStatement ? t.unaryExpression('!', ifExp) : ifExp,
                  originalBody
                );

                // replace the original body with the new if statement
                parentIfStatement.node.consequent = t.blockStatement([innerIfStatement]);
              } else {
              }
            });
            specifier.scope.removeOwnBinding(localBindingName);
            specifier.remove();
          });
          if (path.get('specifiers').length === 0) {
            path.remove();
          }
        }
      },

      Program(path, state) {
        state.importer = new ImportUtil(t, path);
      },
    },
  };
}
